package com.apestech.framework.util;

import com.apestech.framework.json.JSON;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.callback.SpiltStrCallback;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Map;

public class Expression extends StringFuncUtil{

    public class classInfo {
        private String aClassName;
        private ArrayList<String> aFunctionName=new ArrayList<String>();
        private ArrayList<String[]> aParameterTypes=new ArrayList<String[]>();

        public String getaClassName() {
            return aClassName;
        }

        public void setaClassName(String aClassName) {
            this.aClassName = aClassName;
        }

        public void setaFunctionName(String aFunctionName, String[] aParameterTypes) {
            int index=this.aFunctionName.indexOf(aFunctionName);
            if(index<0){
                this.aFunctionName.add(aFunctionName);
                this.aParameterTypes.add(aParameterTypes);
            } else {
                this.aParameterTypes.set(index, aParameterTypes);
            }
        }

        public ArrayList<String> getaFunctionName() {
            return aFunctionName;
        }

        public ArrayList<String[]> getaParameterTypes() {
            return aParameterTypes;
        }
    }

    private ExpressRunner runner;
    private DefaultContext<String, Object> context;
    private String expression;
    private Boolean check=true;
    private JSONObject classlist;
    private int inpos=-1;
    private ArrayList<String> gslist;
    private String vname="";

    public Expression(String expression){
        expression=expression.replace("NULL","null");
        this.expression=expression;
        runner = new ExpressRunner();
        gslist=new ArrayList<>();
        gslist.add(">");
        gslist.add("<");
        gslist.add("+");
        gslist.add("-");
        gslist.add("*");
        gslist.add("/");
    }

    public void setParams(Object... params) throws Exception {
        setParam(params);
    }

    public ExpressRunner getRunner() {
        return runner;
    }

    public void setRunner(ExpressRunner runner) {
        this.runner = runner;
    }

    public void setCheck(Boolean check) {
        this.check = check;
    }

    public Object exec() throws Exception {
        Object r=null;
        try{
            r=runner.execute(expression, context, null, false, false);  //true  false
            runner=null;
            context=null;
        } catch (Exception e){
            String msg=e.toString();
            if(msg.indexOf("匹配")>=0){
                throw new Exception("请检查公式语法的正确性：\n"+expression+"\n"+e.toString());
            } else {
                getNullCaclParam();
                throw new Exception("公式里对应参数["+vname+"]为空值，请补全信息!\n"+expression+"\n"+e.toString());
            }
        }
        return r;
    }

    private void setParam(Object... params) throws Exception {
        context = new DefaultContext<String, Object>();
        if (params != null){
            int ilen=params.length;
            for (int i=0;i<ilen;i++){
                if(params[i]!=null){
                    context.putAll((Map)params[i]);
                }
            }
        }
        if(context.get("array")!=null){
            expression=expression.replace("array", "array1");
            context.put("array1", context.get("array"));
            context.remove("array");
        }
        setExpression();

        try{
            String[] vlist=getExpressVarName();
            int len=vlist.length;
            for(int v=0; v<len; v++){
                String k=vlist[v];
                Object value=context.get(k);
                if(value!=null){
                    String k1=k.toUpperCase()+"#";
                    if(k1.indexOf("_ID#")<0 && k1.indexOf("_NAME#")<0){
                        String s=castNumberToString(value, 8);
                        if(s!=null){
                            context.put(k, new BigDecimal(s));
                        }
                    }
                }
            }
        } catch (Exception e){
        }
    }

    public void getNullCaclParam() throws Exception {
        if(expression.indexOf("?")>=0 && expression.indexOf(":")>=0){
            String[] var=getExpressVarName();
            int vlen=var.length;
            for(int v=0; v<vlen; v++) {
                String key = var[v];
                String value= castToString(context.get(key));
                if(value==null || value.indexOf("null")>=0){
                    if(!"".equals(vname)){
                        vname=vname+",";
                    }
                    vname=vname+key;
                }
            }
        } else {
            inpos=-1;
            int len=expression.length();
            int gslen=gslist.size();
            ArrayList<String> ylist=new ArrayList<>();
            for(int i=0; i<len; i++){
                for(int j=0; j<gslen; j++){
                    String gs=getSubExp(gslist.get(j));
                    if(!"".equals(gs)){
                        String[] var=getExpressVarName(gs);
                        int vlen=var.length;
                        for(int v=0; v<vlen; v++){
                            String key=var[v];
                            String value= castToString(context.get(key));
                            if((value==null || value.indexOf("null")>=0) && !ylist.contains(key)){
                                ylist.add(key);
                                if(!getKeyIniValue(key, -1)){
                                    if(!"".equals(vname)){
                                        vname=vname+",";
                                    }
                                    vname=vname+key;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private boolean getKeyIniValue(String key, int inpos){
        boolean flag=false;
        int pos=expression.indexOf(key+"=", inpos);
        if(pos<0){
            pos=expression.indexOf(key+" =", inpos);
        }
        if(pos>0){
            int prpossem=expression.lastIndexOf(";", pos);
            int unpossem=expression.indexOf(";", pos);
            int lsppos=expression.lastIndexOf("{", pos);
            int rsppos=expression.indexOf("}", pos);
            int lppos=expression.lastIndexOf("(", pos);
            int rppos=expression.indexOf(")", pos);
            int pos1=getPirPos(prpossem, lsppos, lppos);
            int pos2=getNextPos(unpossem, rsppos, rppos);
            String gs=expression.substring(pos1+1, pos2);
            gs=gs.trim();
            if(gs.indexOf("if")>=0){
                return getKeyIniValue(key, pos+1);
            }
            if(gs.indexOf(key)==0){
                if(countString(gs, key)==1){
                    flag=true;
                }
            }
        }
        return flag;
    }

    private String getSubExp(String ch){
        String gs="";
        int pos=expression.indexOf(ch, inpos);
        if(pos>=0){
            int prpossem=expression.lastIndexOf(";", pos);
            int unpossem=expression.indexOf(";", pos);
            int lsppos=expression.lastIndexOf("{", pos);
            int rsppos=expression.indexOf("}", pos);
            int lppos=expression.lastIndexOf("(", pos);
            int rppos=expression.indexOf(")", pos);
            int pos1=getPirPos(prpossem, lsppos, lppos);
            int pos2=getNextPos(unpossem, rsppos, rppos);
            gs=expression.substring(pos1+1, pos2);
            inpos=pos2;
        }
        return gs;
    }

    private int getPirPos(int prpossem, int lsppos, int lppos){  //; { (
        if(prpossem>=0){
            if(prpossem<lsppos && prpossem<lppos){
                if(lsppos>lppos){
                    return lsppos;
                } else {
                    return lppos;
                }
            } else {
                return prpossem;
            }
        } else {
            if(lsppos==-1 && lppos==-1){
                return 0;
            }
            if(lsppos>lppos){
                return lsppos;
            } else {
                return lppos;
            }
        }
    }

    private int getNextPos(int unpossem, int rsppos, int rppos){  //;  } )
        if(unpossem>=0){
            if(unpossem<rsppos && unpossem<rppos){
                return unpossem;
            } else {
                if(rsppos>rppos){
                    return rppos;
                } else {
                    return rsppos;
                }
            }
        } else {
            if(rsppos==-1 && rppos==-1){
                return expression.length();
            }
            if(rsppos>rppos){
                return rppos;
            } else {
                return rsppos;
            }
        }
    }

    public String[] getExpressVarName() throws Exception{
        return getExpressVarName(null);
    }

    public String[] getExpressVarName(String exp) throws Exception{
        if(exp==null){
            exp=expression;
        }
        if(runner==null) runner = new ExpressRunner();
        addFunctionToRun();
        return runner.getOutVarNames(exp);
    }

    private void addFunctionToRun() throws Exception {
        if(classlist==null) return;
        ArrayList<String> klist=classlist.getKeylist();
        int len=klist.size();
        for(int i=0; i<len; i++){
            classInfo c= (classInfo) classlist.get(klist.get(i));
            ArrayList<String> funclist=c.getaFunctionName();
            ArrayList<String[]> plist=c.getaParameterTypes();
            int flen=funclist.size();
            for(int f=0; f<flen; f++){
                runner.addFunctionOfClassMethod(funclist.get(f),c.getaClassName(),funclist.get(f), plist.get(f), null);
            }
        }
    }

    private void setExpression() throws Exception {
        int pos=expression.indexOf("import");
        while(pos>=0){
            if(classlist==null) classlist=new JSONObject();
            int dot=expression.indexOf(";", pos);
            String c=expression.substring(pos+6, dot).trim();
            classInfo cinfo=new classInfo();
            cinfo.setaClassName(c);
            String k=c.substring(c.lastIndexOf(".")+1, c.length());
            expression=expression.substring(0,pos)+expression.substring(dot+1, expression.length());
            setVar(k, cinfo);
            pos=expression.indexOf("import");
        }
    }

    private void setVar(String k, classInfo cinfo) throws Exception {
        int pos=expression.indexOf(k);
        while(pos>=0){
            int pos1=expression.indexOf("(", pos);
            int pos2=expression.indexOf(")", pos1);
            String m=expression.substring(pos+k.length()+1, pos1);
            String p=expression.substring(pos1+1,pos2);
            final String[] pstr = {""};
            execSplitStr(p,",", new SpiltStrCallback(){
                @Override
                public void exec(String id) throws Exception {
                    if(context.get(id)==null){
                        throw new Exception("参数["+id+"]为必填项！");
                    }
                    if(!"".equals(pstr[0])){
                        pstr[0] = pstr[0] +",";
                    }
                    pstr[0] = pstr[0] +context.get(id).getClass().getName();
                }
            });
            cinfo.setaFunctionName(m,pstr[0].split(","));
            expression=expression.substring(0, pos)+expression.substring(pos+k.length()+1,expression.length());
            pos=expression.indexOf(k);
        }
        classlist.put(k,cinfo);
    }

}

